package terra.model.object;

import terra.Config;
import terra.managers.MapManager;
import terra.model.World;
import terra.model.map.Map;
import terra.model.object.interfaces.*;

import terra.model.object.knownlist.ObjectKnownList;
import terra.model.object.map.AbstractMapObject;
import terra.model.object.map.GroundObject;
import terra.model.object.map.MapObject;
import terra.network.packet.Packet;
import terra.network.packet.server.ObjectMove;

public abstract class GameObject extends GObject implements SideInterface,
		Updatable {
	private static final long serialVersionUID = 3317340757990573525L;
	/** координаты объекта */
	private float x, y;
	private float oldX = 0, oldY = 0;

	/** размеры объекта */
	private int width, height;
	/** название картинки объекта */
	private String image;
	private String name;
	/** кноулист объекта. Содержит всё известные объекты. */
	protected ObjectKnownList knownList;
	private boolean remove = false;
	private float yaccel = 0;
	// private int mapIndex;
	private int objectId;

	private boolean isworkplace = false;
	private boolean left = true;

	private int objectMoveNum = Integer.MIN_VALUE;
	transient ObjectMove om = new ObjectMove();

	public GameObject(int objectId) {
		setObjectId(objectId);
	}

	public void setX(float x) {
		setOldX(this.x);
		this.x = x;
	}

	public float getX() {
		return x;
	}

	public void setY(float y) {
		oldY = this.y;
		if (y + getHeight() < Config.tileSize * Config.mapHeight && y >= 0)
			this.y = y;
	}

	public float getY() {
		return y;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getWidth() {
		return width;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	public int getHeight() {
		return height;
	}

	public void setImage(String image) {
		this.image = image;
	}

	public String getImage() {
		return image;
	}

	public void checkLocation() {
		while (isCollision2(getX(), getY()))
			setY(getY() - Config.tileSize / 2);
	}

	public boolean isCollision2(double xx, double yy) {
		int x1 = 0, x2, y1, y2;
		double xx1 = ((xx + 1) / Config.tileSize);
		double xx2 = ((xx + getWidth() - 1) / Config.tileSize);
		x1 = (int) ((xx + 1) / Config.tileSize);
		x2 = (int) ((xx + getWidth() - 1) / Config.tileSize);
		y1 = (int) ((yy + 1) / Config.tileSize);
		y2 = (int) ((yy + getHeight() - 1) / Config.tileSize);
		if (xx1 < 0 && xx1 % Config.tileSize != 0)
			x1--;
		if (xx2 < 0 && xx2 % Config.tileSize != 0)
			x2--;
		for (int x = x1; x <= x2; x++) {
			for (int y = y1; y <= y2; y++) {
				AbstractMapObject go = MapManager.instance.getTileFromTiles(x,
						y);
				if (go == null)
					continue;
				if (go.getType() != MapObject.TYPE_GROUND)
					continue;
				if (!go.canMove())
					return true;
			}
		}
		
		for(GameObject obj : getKnownList().getKnownObjects().values()){
			if (!(obj instanceof Touchable))
					continue;
			if (!((Touchable) obj).isSolid(this))
				continue;
			if (obj.intersects((int)Math.round(xx), (int)Math.round(yy), getWidth(),getHeight()))
				return true;
		}
		return false;
	}
	
	public boolean isCollisionWithObjects(double xx, double yy) {
		for(GameObject obj : getKnownList().getKnownObjects().values()){
			if (!(obj instanceof Touchable))
					continue;
			if (!((Touchable) obj).isSolid(this))
				continue;
			if (obj.intersects((int)Math.round(xx), (int)Math.round(yy), getWidth(),getHeight()))
				return true;
		}
		return false;
	}	

	public GroundObject isCollision(double xx, double yy) {
		int x1 = 0, x2, y1, y2;
		double xx1 = ((xx + 1) / Config.tileSize);
		double xx2 = ((xx + getWidth() - 1) / Config.tileSize);
		x1 = (int) ((xx + 1) / Config.tileSize);
		x2 = (int) ((xx + getWidth() - 1) / Config.tileSize);
		y1 = (int) ((yy + 1) / Config.tileSize);
		y2 = (int) ((yy + getHeight() - 1) / Config.tileSize);
		if (xx1 < 0 && xx1 % Config.tileSize != 0)
			x1--;
		if (xx2 < 0 && xx2 % Config.tileSize != 0)
			x2--;
		for (int x = x1; x <= x2; x++) {
			for (int y = y1; y <= y2; y++) {
				AbstractMapObject go = MapManager.instance.getTileFromTiles(x,
						y);
				if (go == null)
					continue;
				if (go.getType() != MapObject.TYPE_GROUND)
					continue;
				if (!go.canMove())
					return (GroundObject) go;
			}
		}
		return null;
	}

	public void checkTouching() {
		for (GameObject obj : getKnownList().getKnownObjects().values()) {
			if (!(obj instanceof Touchable))
				continue;
			if (obj.intersects((int) getX(), (int) getY(), getWidth(),
					getHeight()))
				((Touchable) obj).onTouch(this);
		}
	}

	public double distanceTo(GameObject obj) {
		float dx = obj.getX() - getX();
		float dy = obj.getY() - getY();
		return Math.sqrt((dx * dx) + (dy * dy));
	}

	public double distanceTo(MapObject obj) {
		float dx = obj.getX() - getX();
		float dy = obj.getY() - getY();
		return Math.sqrt((dx * dx) + (dy * dy));
	}

	public double distanceTo(float x, float y) {
		float dx = x - getX();
		float dy = y - getY();
		return Math.sqrt((dx * dx) + (dy * dy));
	}

	public void needUpdate() {
		needUpdate(this);
	}

	public float getYacceleration() {
		return yaccel;
	}

	public void setYacceleration(float yaccel) {
		this.yaccel = yaccel;
	}

	public abstract ObjectKnownList getKnownList();

	// public void setKnownList(ObjectKnownList knownList) {
	// this.knownList = knownList;
	// }

	public int getMapIndex() {
		return MapManager.getInstance().getMapIndex(
				(int) (getX() / Config.tileSize));
	}

	// public void setMapIndex(int mapIndex) {
	// this.mapIndex = mapIndex;
	// }

	public boolean isRemove() {
		return remove;
	}

	public void setRemove(boolean remove) {
		this.remove = remove;
	}

	public int getObjectId() {
		return objectId;
	}

	public void setObjectId(int objectId) {
		this.objectId = objectId;
	}

	public boolean isWorkPlace() {
		return isworkplace;
	}

	public void setWorkplace(boolean isCraftModifier) {
		this.isworkplace = isCraftModifier;
	}

	public boolean isLeft() {
		return left;
	}

	public void setLeft(boolean left) {
		this.left = left;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public boolean intersects(int x, int y, int w, int h) {
		if ((x > (int) (getX() + getWidth())) || ((x + w) < (int) getX()))
			return false;
		if ((y > (int) (getY() + getHeight())) || ((y + h) < (int) getY()))
			return false;
		return true;
	}

	public static boolean intersects(int x1, int y1, int w1, int h1, int x2,
			int y2, int w2, int h2) {
		if (x1 > x2 + w2 || x1 + w1 < x2) {
			return false;
		}
		if (y1 > y2 + h2 || y1 + h1 < y2) {
			return false;
		}
		return true;
	}

	public static void needUpdate(GameObject object) {
		if (object == null)
			return;
		if (!object.isRemove())
			World.getInstance().addUpdateObject(object);
	}

	public ObjectMove getObjectMove() {
		// om.clearBuffer();
		if (om == null)
			om = new ObjectMove();
		om.oId = getObjectId();
		om.x = getX();
		om.y = getY();
		om.num = getObjectMoveNum();
		om.left = this.isLeft();
		return om;
	}

	public boolean positionChanged() {
		if (getOldX() != getX())
			return true;
		if (oldY != getY())
			return true;
		return false;
	}

	public float getOldX() {
		return oldX;
	}

	public void setOldX(float oldX) {
		this.oldX = oldX;
	}

	public int getObjectMoveNum() {
		objectMoveNum++;
		if (objectMoveNum + 1 == Integer.MAX_VALUE)
			objectMoveNum = Integer.MIN_VALUE;
		return objectMoveNum;
	}

	public float getOldY() {
		return oldY;
	}

	public void setOldY(float oldY) {
		this.oldY = oldY;
	}

	public Map getMap() {
		return MapManager.getInstance().getMap(getMapIndex());
	}

	transient Packet info;

	public abstract Packet getInfo();

	public void broadcastPacket(Packet p) {
		for (GameObject obj : getKnownList().getKnownObjects().values()) {
			if (obj instanceof Player) {
				((Player) obj).send(p);
			}
		}
	}
}